#include "CLFile.h"

using namespace std;

#define DEBUG 1

#define True 1
#define False 0

#define READ_BUFFER_SIZE 8
#define WRITE_BUFFER_SIZE 8

//检查两个区间是否有重叠的功能函数
int checkOverlay(off_t begin1, off_t end1, off_t begin2, off_t end2);

CLFile::CLFile()
{
	mFd = -1;
	
	curPos = 0;
	noReadBuffer = True;
	noWriteBuffer = True;
	isAppend = False;
	
	readBuffer = new char[READ_BUFFER_SIZE];
	readBegin = readEnd = readPosition = 0;
	
	writeBuffer = new char[WRITE_BUFFER_SIZE];
	writeBegin = writeEnd = writePosition = 0;
}

CLFile::~CLFile()
{
	delete readBuffer;
	delete writeBuffer;
}

int CLFile::Open(const char *pathname, int flags)
{
	int fd;

	fd = open(pathname, flags);

	if(fd < 0)
	{
		if(DEBUG)
			cout << "Open file error: " << strerror(errno) << endl;

		return -1;
	}
	
	if(flags & O_APPEND)
		this->isAppend = True;
		
	this->mFd = fd;
		
	if(DEBUG)
		cout << "File opened done." << endl;
	
	return fd;
}

off_t CLFile::Lseek(off_t offset, int whence)
{
	off_t pos;

	pos = lseek(this->mFd, offset, whence);

	if(pos < 0)
	{
		if(DEBUG)
			cout << "Lseek file error: " << strerror(errno) << endl;

		this->Close();

		return -1;
	}

	this->curPos = pos;

	return pos;
}

int CLFile::Close()
{
	int ret;

	if(DEBUG)
		cout << "Check writeBuffer if there is some data." << endl;
	
	if(writeBegin != writePosition)
	{
		if(DEBUG)
			cout << "Need to flush writeBuffer before closing file." << endl;
		this->flushWriteBuffer();
	}

	ret = close(this->mFd);

	if(ret < 0)
	{
		if(DEBUG)
			cout << "File close error: " << strerror(errno) << endl;

		return -1;
	}
	
	if(DEBUG)
		cout << "File closed." << endl;
	
	return ret;
}


/*
 更新读缓存的具体步骤：
 首先，设置readBuffer的一些参数：
 	起始文件偏移量 readBegin = this->curPos
	读缓存时当前文件偏移量 readPosition = this->curPos
 然后，从文件中读取READ_BUFFER_SIZE大小的数据进入readBuffer
 其次，获取当前文件偏移量（实际的read的操作才会更改文件偏移量）
 	设置末尾文件偏移量为当前文件偏移量this->curPos
 最后，我们还原当前实际文件偏移量为读缓存时当前文件偏移量
 */
void CLFile::updateReadBuffer()
{
	if(this->mFd < 0)
	{
		cout << "No file opened." << endl;
		return;
	}
	
	this->readBegin = this->curPos;
	this->readPosition = this->curPos;

	if(DEBUG)
	{
		cout << "Update readBuffer: " << endl;
		cout << "Read data from disk file into read buffer." << endl;
	}

	// read函数会更改当前文件偏移量
	if(read(mFd, this->readBuffer, READ_BUFFER_SIZE) < 0)
	{
		cout << "Read from file error: " << strerror(errno) << endl;
		return;
	}

	// 获取当前文件偏移量
 	this->Lseek(0, SEEK_CUR);
	this->readEnd = this->curPos;

	// 重设当前文件偏移量为读缓存当前文件偏移量
	this->Lseek(this->readPosition, SEEK_SET);

	if(DEBUG)
		cout << "End update readBuffer." << endl;
}

ssize_t CLFile::Read(void *buf, size_t count)
{
	int flushFlag = 0;
	ssize_t readCnt = 0;

	if(DEBUG)
		cout << "----Read Operation---->>>>" << endl;

	if(count <= 0)
	{
		if(DEBUG)
			cout << "Wrong parameter: count must be positive." << endl;

		return -1;
	}

	if(this->mFd < 0)
	{
		if(DEBUG)
			cout << "No file opened." << endl;
		return -1;
	}

	/* 
	 获取当前文件偏移量，即获取Read操作的起始文件偏移量
	 因此===>>>>
	 “读操作文件偏移量范围” --> (this->curPos, this->curPos + count)
	 “写缓存的有效文件偏移量范围” --> (this->writeBegin, this->writePosition)
	 */ 
	this->Lseek(0, SEEK_CUR);
	if(DEBUG)
	{
		cout << "Read operation offset range: " << this->curPos << " - " << this->curPos + count << endl;
		cout << "Write buffer file offset range: " << this->writeBegin << " - " << this->writePosition << endl;
	}

	// 判断写缓存是否有数据
	// 判断写缓存有效文件偏移量是否与读文件操作文件偏移量范围冲突
	// 有冲突且有数据 ===>>> 刷新写缓存
	if( (writeBegin != writePosition) && 
		(checkOverlay(this->curPos, this->curPos + count, this->writeBegin, this->writePosition) > 0) )
	{
			if(DEBUG)
				cout << "Data in writeBuffer must be flushed." << endl;

			this->flushWriteBuffer();
			flushFlag = 1;
	}

	if(count > READ_BUFFER_SIZE)
	{
		if(DEBUG)
			cout << "Read data directly from file on disk." << endl;

		readCnt = read(mFd, buf, count);

		if(readCnt < 0)
		{
			if(DEBUG)
				cout << "Read data from fd error: " << strerror(errno) << endl;

			return -1;
		}

		if(DEBUG)
			cout << "Read data directly from file completed." << endl;
		
		return readCnt;
	}
	
	if(noReadBuffer == True)
	{
		if(DEBUG)
			cout << "Update readBuffer first of all." << endl;

		this->updateReadBuffer();
		noReadBuffer = False;
	}
	
	if(this->curPos >= this->readBegin && this->curPos + count <= this->readEnd)
	{
		/**
		----------------------------------------------------
		               |       Read Buffer      |
		----------------------------------------------------
		                    |  Read range   |
		----------------------------------------------------
		*/
		if(DEBUG)
			cout << "Read data directly from readBuffer." << endl;
		
		if(flushFlag)
			this->updateReadBuffer();

		off_t offset = this->curPos - this->readBegin;
		memcpy(buf, readBuffer + offset, count);
		this->readPosition = this->curPos + count;

		// 设置当前文件偏移量
		this->Lseek(this->readPosition, SEEK_SET);
		readCnt = count;
	}
	else
	{
		/**
		----------------------------------------------------
		               |       Read Buffer      |
		----------------------------------------------------
		情形1:     |  Read range   |
		----------------------------------------------------
		情形2:                  |     Read range      |
		----------------------------------------------------
		**/
		if(DEBUG)
		{
			cout << "Read operation range not in readBuffer." << endl;
		}

		this->updateReadBuffer();

		if(this->readEnd - this->curPos < count )
		{
			if(DEBUG)
				cout << "Not such much data to read." << endl;

			return -1;
		}

		memcpy(buf, readBuffer, count);
		this->readPosition = this->curPos + count;

		// 设置当前文件偏移量
		this->Lseek(this->readPosition, SEEK_SET);
		readCnt = count;
	}
	
	return readCnt;
}


/*
 对于写缓存：O_APPEND和非O_APPEND
 --------------------------------
 非O_APPEND：可以在任意的文件偏移量处写数据
 --------------------------------
 O_APPEND：只能在文件末尾写数据
 */
ssize_t CLFile::Write(const void* buf, size_t count)
{
	ssize_t writeCnt, leftRoom;

	if(DEBUG)
		cout << "----Write Operation---->>>>" << endl;

	if(this->mFd < 0)
	{
		if(DEBUG)
			cout << "No file opened." << endl;

		return -1;
	}

	// 获取当前文件偏移量
	this->Lseek(0, SEEK_CUR);
		
	if(count > WRITE_BUFFER_SIZE)
	{
		if(DEBUG)
			cout << "Write directly to disk file." << endl;
		
		if(this->writeBegin != this->writePosition)
			this->flushWriteBuffer();
			
		writeCnt = write(mFd, buf, count);
			
		if(writeCnt < 0)
		{
			if(DEBUG)
				cout << "Write to disk file error: " << strerror(errno) << endl;

			return -1;
		}
		
		if(DEBUG)
			cout << "Write directly to file completed." << endl;
		return writeCnt;
	}
		
	/*
	 --------------------------------------------------------------------------------
	   ||||
	   ||||
	   vvvv
	 写的数据长度肯定小于写缓存大小！！！因此，下一次写必然能够填入到writeBuffer！！！
	 ---------------------------------------------------------------------------------
	 什么情况下需要重新建立writeBuffer呢？
	 一句话：flushWriteBuffer后的下一次写（除了超长度之外）时就必须重新建立WriteBuffer
	 */
	if(this->noWriteBuffer == True)
	{
		if(DEBUG)
			cout << "Build new writeBuffer to cache data." << endl;
			
		/*
		 *以当前文件偏移量建立writeBuffer
		 设置起始文件偏移量---( curPos, curPos + WRITE_BUFFER_SIZE )
		 设置noWriteBuffer为假
		 */
		if(isAppend)
			this->Lseek(0, SEEK_END);
		else
			this->Lseek(0, SEEK_CUR);

		this->writeBegin = this->curPos;
		this->writePosition = this->curPos;
		this->writeEnd = this->writeBegin + WRITE_BUFFER_SIZE;
		this->noWriteBuffer = False;

		// 将数据写入到writeBuffer
		memcpy(this->writeBuffer, buf, count);

		writeCnt = count;
		this->writePosition = this->writeBegin + count;

		// 设置当前文件偏移量为写缓存操作文件偏移量
		this->Lseek(this->writePosition, SEEK_SET);

		if(DEBUG)
			cout << "Write to new writeBuffer completed." << endl;

		return writeCnt;
	}
	
	if(isAppend)
		this->Lseek(this->writePosition, SEEK_SET);

	if(this->curPos >= this->writeBegin && this->curPos + count <= this->writeEnd)
	{
		// writeBuffer可用容量能够承载所写数据量

		if(DEBUG)
			cout << "Write data directly into existed writeBuffer." << endl;

		off_t offset = this->curPos - this->writeBegin;

		// 将数据写入到writeBuffer
		memcpy(this->writeBuffer + offset, buf, count);

		writeCnt = count;
		this->writePosition = this->curPos + count;
		this->Lseek(this->writePosition, SEEK_SET);

		if(DEBUG)
			cout << "Write data directly into existed writeBuffer completed." << endl;
	}
	else
	{
		// 新写入数据的偏移量不在writeBuffer之内，或者数据量超过剩余可用容量

		if(DEBUG)
			cout << "Write data to a new buffer after flushing old writeBuffer." << endl;

		this->flushWriteBuffer();
			
		if(isAppend)
			this->Lseek(0, SEEK_END);
		else
			this->Lseek(0, SEEK_CUR);

		this->writeBegin = this->curPos;
		this->writePosition = this->curPos;
		this->writeEnd = this->writeBegin + WRITE_BUFFER_SIZE;
		this->noWriteBuffer = 0;

		memcpy(this->writeBuffer, buf, count);
		writeCnt = count;
		this->writePosition = this->curPos + count;
		this->Lseek(this->writePosition, SEEK_SET);

		if(DEBUG)
			cout << "Write data to a new buffer after flushing old writeBuffer completed." << endl;
	}
	
	return writeCnt;
}


void CLFile::flushWriteBuffer()
{
	// 当flushWriteBuffer时，需要保存当前文件偏移量，以便于恢复
	off_t tmpPos = this->Lseek(0, SEEK_CUR);

	if(this->writeBegin != this->writePosition)
		this->Lseek(this->writeBegin, SEEK_SET);
	
	if(DEBUG)
		cout << "WriteBuffer flushing......" << endl;
	
	if(this->mFd < 0)
	{
		if(DEBUG)
			cout << "No file opened." << endl;

		return;
	}
	
	if(this->writeBegin == this->writePosition)
	{
		if(DEBUG)
			cout << "Empty in writeBuffer, no need to flush." << endl;

		return;
	}
	
	if(DEBUG)
		cout << "Buffer content to write: " << this->writeBuffer << endl;
	
	unsigned int cnt = this->writePosition - this->writeBegin;
	
	if(DEBUG)
		cout << "Buffer len to write: " << cnt << endl;

	ssize_t r = write(mFd, this->writeBuffer, cnt);

	if(r == -1)
	{
		if(DEBUG)
			cout << "Write to disk file error: " << strerror(errno) << endl;

		return;
	}
	
	this->writeBegin = this->writeEnd = this->writePosition = 0;

	this->noWriteBuffer = 1;
	
	// 还原文件偏移量
	this->Lseek(tmpPos, SEEK_SET);

	if(DEBUG)
		cout << "WriteBuffer flush completed." << endl;

	return;
}

int checkOverlay(off_t begin1, off_t end1, off_t begin2, off_t end2)
{
	off_t begin = (begin1 > begin2) ? begin1 : begin2;
	off_t end = (end1 < end2) ? end1 : end2;
	return (end-begin);
}